﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Data.OleDb;
using AliCMS.SystemModule;

namespace AliCMS.TemplateEngine
{
    internal class Ad : Tag
    {
        internal Ad(Engine engine, int index, int length, int contentIndex, int contentLength, Tag parent)
            : base(engine, index, length, contentIndex, contentLength, parent)
        {
        }

        internal override void Init()
        {
            #region Mark
			if (Engine.IsMultiSite)
			{
				_mark = "Ad";
			}
			else
			{
				_mark = String.Empty;
				Regex re = new Regex("Mark=\"(?<v>.*?)\"", RegexOptions.Compiled);
				Match match = re.Match(Engine.Text, Index, Length);
				if (match.Success)
					_mark = match.Groups["v"].Value;
			}
            #endregion
        }
        internal override void Parse()
        {
            ParseToCode();
            ParseToXml();
        }
        private string ParseCondition(string condition)
        {
            StringBuilder result = new StringBuilder();

            result.Append("string condition = null;\r\n");

            DataRow[] objects = Manager.Objects.Select(Alias.Module.Object.Namespace + "='AliCMS.AdModule' and FullName='Ad'");
            DataRow[] properties = Manager.Properties.Select(Alias.Module.Object.Property.ObjectID + "='" + objects[0][Alias.Module.Object.ID] + "'");

            #region 排序
            DataTable propertyTable = new DataTable();
            propertyTable.Columns.Add("Name", typeof(string));
            propertyTable.Columns.Add("Alias", typeof(string));
            propertyTable.Columns.Add("OleDbType", typeof(byte));
            propertyTable.Columns.Add("Width", typeof(int));

            foreach (DataRow property in properties)
            {
                DataRow newRow = propertyTable.NewRow();
                newRow["Name"] = property[Alias.Module.Object.Property.Name];
                newRow["Alias"] = property[Alias.Module.Object.Property.UniqueInnerID];
                newRow["OleDbType"] = property[Alias.Module.Object.Property.OleDbType];
                newRow["Width"] = newRow["Name"].ToString().Length;
                propertyTable.Rows.Add(newRow);
            }

            properties = propertyTable.Select("", "Width DESC");
            #endregion

            #region 获取类型
            Dictionary<int, OleDbType> types = new Dictionary<int, OleDbType>();
            MatchCollection matches = Regex.Matches(condition, @"(?<n>[a-zA-Z_]\w*)\s?(=|>|<|<>|>=|<=)\s?(?<v>[a-zA-Z_]\w*\[[a-zA-Z_]\w*\])");
            foreach (Match m in matches)
            {
                DataRow[] rows = propertyTable.Select("Name='" + m.Groups["n"].Value + "'");
                if (rows.Length == 0 || Convert.IsDBNull(rows[0]["OleDbType"]))
                    continue;

                types.Add(m.Groups["v"].Index, (OleDbType)(byte)rows[0]["OleDbType"]);
            }
            #endregion

            bool hasLastParenthis = false;
            int start = 0;
            int end = 0;
            matches = Regex.Matches(condition, @"(\)?\s+(and|or)\s+(\(\s?)?)?(?<n>\w+)\s*(>=|<=|<>|=|>|<|\sLIKE\s|\sIS NOT\s|\sIS\s)\s*", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            for (int i = 0; i < matches.Count; i++)
            {
                Group nameGroup = matches[i].Groups["n"];
                string name = nameGroup.Value;
                string alias = name;
                foreach (DataRow property in properties)
                {
                    if (property["Name"].ToString() == name)
                    {
                        alias = property["Alias"].ToString();
                        break;
                    }
                }

                string temp = matches[i].Value.Remove(nameGroup.Index - matches[i].Index, nameGroup.Length);
                temp = temp.Insert(nameGroup.Index - matches[i].Index, alias);
                result.Append("condition += \"" + temp + "\";\r\n");

                start = matches[i].Index + matches[i].Length;
                if (i < matches.Count - 1)
                    end = matches[i + 1].Index;
                else
                    end = condition.Length;
                string value = condition.Substring(start, end - start);

                if (i == matches.Count - 1 && value.EndsWith(")"))
                {
                    value = value.Remove(value.Length - 1);
                    hasLastParenthis = true;
                }

                #region 已知常量
                if (Regex.IsMatch(value, @"^(false|true|NULL|\d+|\{?[a-fA-F0-9]{8}(-?[a-fA-F0-9]{4}){3}-?[a-fA-F0-9]{12}\}?|'[^']*')$", RegexOptions.Compiled | RegexOptions.IgnoreCase))
                {
                    result.Append("condition += \"" + value + "\";\r\n");
                    continue;
                }
                #endregion

                DataRow[] rows = propertyTable.Select("Name='" + name + "'");
                if (rows.Length == 0 || Convert.IsDBNull(rows[0]["OleDbType"]))
                {
                    result.Append("condition += " + Engine.ParseExpression(this.Parent == null ? this : this.Parent, value) + ";\r\n");
                }
                else
                {
                    OleDbType type = (OleDbType)(byte)rows[0]["OleDbType"];
                    if (type == OleDbType.Guid)
                    {
                        result.Append("object v" + i + " = " + Engine.ParseExpression(this.Parent == null ? this : this.Parent, value) + ";");
                        result.Append(@"
                                        if (v" + i + @" == null)
                                            condition = condition.Substring(0, condition.Length - " + (temp.StartsWith(")") ? temp.Length - 1 : temp.Length) + @");
                                        else if (v" + i + @".ToString() == String.Empty)
                                            condition = condition.Substring(0, condition.Length - 1) + "" IS NULL "";
                                        else
                                            condition += v" + i + @".ToString();
                                        ");
                    }
                    else if (type == OleDbType.BSTR || type == OleDbType.Char || type == OleDbType.LongVarChar || type == OleDbType.LongVarWChar || type == OleDbType.VarChar || type == OleDbType.VarWChar || type == OleDbType.WChar)
                    {
                        result.Append("condition += \"'\" + " + Engine.ParseExpression(this.Parent == null ? this : this.Parent, value) + " + \"'\";\r\n");
                    }
                    else if (type == OleDbType.Date || type == OleDbType.DBDate || type == OleDbType.DBTime || type == OleDbType.DBTimeStamp)
                    {
                        result.Append("condition += \"#\" + Convert.ToDateTime(" + Engine.ParseExpression(this.Parent == null ? this : this.Parent, value) + ").ToString(\"yyyy-MM-dd HH:mm:ss\") + \"#\";\r\n");
                    }
                    else
                    {
                        result.Append("condition += " + Engine.ParseExpression(this.Parent == null ? this : this.Parent, value) + ";\r\n");
                    }
                }
            }

            if (matches.Count == 0)
                result.Append("condition += " + Engine.ParseExpression(this.Parent == null ? this : this.Parent, condition) + ";\r\n");

            if (hasLastParenthis)
                result.Append("condition += \")\";");

            return result.ToString();
        }
        private void ParseToCode()
        {
            StringBuilder result = new StringBuilder();
            Regex re = null;
            Match match = null;

            #region InstanceID
            string instanceID = String.Empty;
            if (String.IsNullOrEmpty(_mark))
            {
                instanceID += "DataRow[] rows = Manager.Instances.Select(AliCMS.SystemModule.Alias.Module.Namespace + \"='AliCMS.AdModule'\");";
                instanceID += "if (rows.Length == 0)\r\n{instanceID = 0;}\r\nelse\r\n{instanceID = (int)rows[0][AliCMS.SystemModule.Alias.Instance.InnerID];}";
            }
            else
            {
                instanceID += "DataRow channel = " + Engine.ParseChannel(_mark) + ";";
                instanceID += "if (channel == null)\r\n{instanceID = 0;}\r\nelse\r\n{instanceID = (int)channel[AliCMS.SystemModule.Alias.Instance.InnerID];}";
            }
            #endregion

            #region Guid
            re = new Regex("(Guid|No)=\"(?<g>.*?)\"", RegexOptions.Compiled);
            match = re.Match(Engine.Text, Index, Length);
            if (!match.Success)
            {
				Exception exp = new Exception("lang_parse_template_innerid_or_guid_must_input");
				Engine.ReportError(exp, Index);
            }
            string guid = match.Groups["g"].Value;
            #endregion

            #region GenerateCacheFile
            string generateCacheFile = String.Empty;
            if (_firstAd)
            {
                generateCacheFile += @"
                            string path = String.Empty;
                            using (AliCMS.AdModule.IInstanceConfig configBll = (AliCMS.AdModule.IInstanceConfig)Manager.CreateObject(""InstanceConfig"", instanceID, PageLang, false))
                            {
                                Dictionary<string, string> config = configBll.GetBasicSettings();
                                path = config[AliCMS.AdModule.Alias.InstanceConfig.AdCacheFile].ToString();
                                if (!System.IO.File.Exists(Manager.MapPath(path)))
                                {
                                    configBll.GenerateCacheFile();
                                }
                            }
                            ";
            }
            #endregion

            #region Default
            StringBuilder de = new StringBuilder();
            foreach (Tag tag in ChildTags)
            {
                if (tag is Code)
                {
                    de.Append(tag.ResultCode);
                }
                else if (tag is OutputStatment)
                {
                    de.Append("object " + tag.UniqueName + " = " + tag.ResultCode + ";\r\n");
                    de.Append("result.Replace(\"{#" + tag.UniqueName + "#}\"," + tag.UniqueName + " == null ? \"\" : " + tag.UniqueName + ".ToString());\r\n");
                }
                else if (tag is IfStatment)
                {
                    de.Append(tag.ResultCode);
                    de.Append("result.Replace(\"{#" + tag.UniqueName + "#}\"," + tag.UniqueName + ");\r\n");
                }
                else if (tag.ResultCode != null && Regex.IsMatch(tag.ResultCode, "protected string " + tag.UniqueName + @"\s+{\s+get\s+"))
                {
                    de.Append("result.Replace(\"{#" + tag.UniqueName + "#}\"," + tag.UniqueName + ");\r\n");
                }
            }
            #endregion

            #region Condition
            string condition = String.Empty;
            re = new Regex("Condition=\"(?<v>.*?)\"", RegexOptions.Compiled);
            match = re.Match(Engine.Text, Index, Length);
            if (match.Success)
                condition = ParseCondition(match.Groups["v"].Value);
            #endregion

            #region Custom
            #region Data
            string data = String.Empty;
            re = new Regex("Data=\"(?<v>.*?)\"", RegexOptions.Compiled);
            match = re.Match(Engine.Text, Index, Length);
            if (match.Success)
            {
                if (match.Groups["v"].Value.StartsWith("{") && match.Groups["v"].Value.EndsWith("}"))
                    data = "string data = \"" + match.Groups["v"].Value + "\";";
                else
                    data = "string data = " + Engine.CreateExpression(this, match.Groups["v"].Value).Parse() + ";";
            }
            if (Engine.IsMultiSite && data == "")
                data = "string data = TEBuildAdData(instanceID, \"" + guid + "\");";
            #endregion

            #region TypeID
            string typeID = String.Empty;
            re = new Regex("TypeID=\"(?<v>.*?)\"", RegexOptions.Compiled);
            match = re.Match(Engine.Text, Index, Length);
            if (match.Success)
            {
                if (match.Groups["v"].Value.StartsWith("{") && match.Groups["v"].Value.EndsWith("}"))
                    typeID = "string typeID = \"" + match.Groups["v"].Value.Replace("{", "").Replace("}", "").Replace("-", "").ToLower() + "\";";
                else
                    typeID = "string typeID = " + Engine.CreateExpression(this, match.Groups["v"].Value).Parse() + ";";
            }

            if (Engine.IsMultiSite && typeID == "")
                typeID = "string typeID = TEGetAdTypeID(instanceID, \"" + guid + "\");";
            #endregion

            string custom = String.Empty;
            if (typeID != "" && data != "")
            {
                custom = typeID + data;
                custom += "if (!String.IsNullOrEmpty(typeID) && !String.IsNullOrEmpty(data))return \"<%WriteJs%><script>new T\" + typeID.Replace(\"{\", \"\").Replace(\"}\", \"\").Replace(\"-\", \"\").ToLower() + \"(\" + data + \").Write();</script>\";";
            }
            #endregion

            #region Invoke
            StringBuilder invoke = new StringBuilder();
            if (condition != String.Empty)
            {
                invoke.Append(condition);
                invoke.Append("result.Append(\"<script>\" + positionBll.BuildInvoke(id, condition) + \"</script>\");");
            }
            else
            {
                invoke.Append("result.Append(\"<script>P\" + id + \"();</script>\");");
            }
            #endregion

            result.Append(@"
                            protected string <%Name%>Default
                            {
                                get
                                {
                                    StringBuilder result = new StringBuilder(_xmlTemplate.SelectSingleNode(""//<%Name%>"").InnerText);
                                    <%Default%>
                                    return result.ToString();
                                }
                            }
                            protected string <%Name%>
                            {
                                get
                                {
                                    int instanceID;
                                    <%InstanceID%>
                                    if (instanceID == 0)
                                        return <%Name%>Default;

                                    <%GenerateCacheFile%>

                                    <%Custom%>

                                    AliCMS.AdModule.IPosition positionBll = (AliCMS.AdModule.IPosition)Manager.CreateObject(""Position"", instanceID, PageLang, false);
									string positionIdOrNo = ""<%Guid%>"";
									DataTable table;
									if (System.Text.RegularExpressions.Regex.IsMatch(positionIdOrNo, @""\{?[a-fA-F0-9]{8}(-?[a-fA-F0-9]{4}){3}-?[a-fA-F0-9]{12}\}?""))
										table = positionBll.List(1, 1, AliCMS.AdModule.Alias.Position.ID + ""="" + positionIdOrNo, null);
									else
										table = positionBll.List(1, 1, AliCMS.AdModule.Alias.Position.SubInstanceID + ""="" + Request.QueryString[""DynamicAdSubInstanceID""] + "" AND "" + AliCMS.AdModule.Alias.Position.No + ""='"" + positionIdOrNo + ""'"", null);

                                    StringBuilder result = new StringBuilder(""<%WriteJs%>"");
                                    if (table.Rows.Count != 0 && !Convert.ToBoolean(table.Rows[0][AliCMS.AdModule.Alias.Position.IsDeleted]))
                                    {
                                        int id = (int)table.Rows[0][AliCMS.AdModule.Alias.Position.InnerID];
                                        <%Invoke%>;
                                    }
                                    else
                                    {
                                        result.Append(<%Name%>Default);
                                    }
                                    positionBll.Dispose();
                                    return result.ToString();
                                }
                            }
                            ");
            result.Replace("<%Name%>", UniqueName);
            result.Replace("<%Custom%>", custom);
            result.Replace("<%InstanceID%>", instanceID);
            result.Replace("<%Guid%>", guid);
            result.Replace("<%GenerateCacheFile%>", generateCacheFile);
            if (_firstAd)
                result.Replace("<%WriteJs%>", "<script src=\\\"\" + path + \"\\\"></script>");
            else
                result.Replace("<%WriteJs%>", "");
            result.Replace("<%Invoke%>", invoke.ToString());
            result.Replace("<%Default%>", de.ToString());
            ResultCode = result.ToString();
        }
        private void ParseToXml()
        {
            StringBuilder all = new StringBuilder(Engine.Text.Substring(ContentIndex, ContentLength));
            for (int i = ChildTags.Count - 1; i >= 0; i--)
            {
                Tag tag = ChildTags[i];
                all.Remove(tag.Index - ContentIndex, tag.Length);
                if (tag is IfStatment || tag is OutputStatment || (tag.ResultCode != null && Regex.IsMatch(tag.ResultCode, "protected string " + tag.UniqueName + @"\s+{\s+get\s+")))
                    all.Insert(tag.Index - ContentIndex, "{#" + tag.UniqueName + "#}");
            }

            ResultXml = "<![CDATA[" + all + "]]>";
        }

        #region Properties
        public bool FirstAd
        {
            get { return _firstAd; }
            set { _firstAd = value; }
        }
        #endregion

        #region Fields
        private string _mark;
        private bool _firstAd;
        #endregion
    }
}
